package gui.batches;

import core.ContainerType;
import core.CoreFacade;
import core.Item;
import core.Product;
import core.ProductContainer;
import core.StorageUnit;
import gui.common.*;
import gui.inventory.*;
import gui.item.ItemData;
import gui.product.*;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.Timer;

/**
 * Controller class for the transfer item batch view.
 */
public class TransferItemBatchController extends Controller implements
		ITransferItemBatchController {
    
    private boolean scannerEnabled;
    private CoreFacade model;
    private List<Product> productsEffected;
    private Map<Product, ArrayList<ItemData>> transferredItems;
    private StorageUnit targetPC;
    
    private Timer scannerTimer = new Timer(500,
			
			new ActionListener()
			{
				@Override
				public void actionPerformed(ActionEvent arg0) 
				{
                                    transferItem();
				}
		
			}
			
			);
	
	/**
	 * Constructor.
	 * 
	 * @param view Reference to the transfer item batch view.
	 * @param target Reference to the storage unit to which items are being transferred.
	 */
	public TransferItemBatchController(IView view, ProductContainerData target) {
		super(view);
                
        scannerEnabled = false;
        model = CoreFacade.getInstance();
        model.clearHistory();
        productsEffected = new ArrayList<Product>();
        transferredItems = new HashMap<Product, ArrayList<ItemData>>();
        targetPC = (StorageUnit) target.getTag();
		construct();
		getView().setUseScanner(true);
	}
	
	/**
	 * Returns a reference to the view for this controller.
	 */
	@Override
	protected ITransferItemBatchView getView() {
		return (ITransferItemBatchView)super.getView();
	}

	/**
	 * Loads data into the controller's view.
	 * 
	 *  {@pre None}
	 *  
	 *  {@post The controller has loaded data into its view}
	 */
	@Override
	protected void loadValues() 
    {
		Product selected = null;
		ProductData newSelected = null;
		
		if(getView().getSelectedProduct() != null)
		{
			selected = (Product)getView().getSelectedProduct().getTag();
		}
        
        ProductData[] productDataToBeRemoved = new ProductData[productsEffected.size()];
        
        for(int i = 0; i < productsEffected.size(); i++)
        {
            ProductData pd = convertToProductData(productsEffected.get(i), 
            					transferredItems.get(productsEffected.get(i)).size());
            productDataToBeRemoved[i] = pd;
            
            if((selected != null) &&
                    (productsEffected.get(i).getDescription().compareTo(
                    									selected.getDescription()) == 0))
                 {
                 	newSelected = productDataToBeRemoved[i];
                 }
        }
                
        getView().setProducts(productDataToBeRemoved);
        
        getView().selectProduct(newSelected);
	}

	private void reloadItems()
	{
		
		if (getView().getSelectedProduct() == null)
		{
			ItemData[] empty = new ItemData[0];
			getView().setItems(empty);
			return;
		}
		
		Item selectedItem = null;
		ItemData newSelect = null;
		
		if (getView().getSelectedItem() != null)
		{
			selectedItem = (Item)getView().getSelectedItem().getTag();
		}
		
		Product p = model.getProduct(getView().getSelectedProduct().getBarcode());
				
		ArrayList<ItemData> theItems = transferredItems.get(p);
		
		
		
		if (theItems == null) 
		{
			return;
		}
		
		if (theItems.size() == 0) 
		{
			return;
		}
		
		ItemData[] addedItemsData = new ItemData[theItems.size()];
		
		for (int i = 0; i < theItems.size(); i++)
		{
			addedItemsData[i] = theItems.get(i);
			
			if ((selectedItem != null) && 
				(selectedItem.getBarcode().equals(theItems.get(i).getBarcode()) == true))
			{
				newSelect = addedItemsData[i];
			}
		}
		
		getView().setItems(addedItemsData);
		
		getView().selectItem(newSelect);
		
	}
	/**
	 * Sets the enable/disable state of all components in the controller's view.
	 * A component should be enabled only if the user is currently
	 * allowed to interact with that component.
	 * 
	 * {@pre None}
	 * 
	 * {@post The enable/disable state of all components in the controller's view
	 * have been set appropriately.}
	 */
	@Override
	protected void enableComponents() 
       {
            getView().enableUndo(model.canUndo());
            getView().enableRedo(model.canRedo());
            getView().enableItemAction(false);
//            getView().setUseScanner(true);
	}
	
	/**
	 * This method is called when any of the fields in the
	 * transfer item batch view have been changed by the user.
	 */
	@Override
	public void valuesChanged()
	{
		enableComponents();
	}

	/**
	 * This method is called when the "Item Barcode" field in the
	 * transfer item batch view is changed by the user.
	 */
	@Override
	public void barcodeChanged() 
        {
            if(getView().getUseScanner())
            {
                if (scannerTimer.isRunning())
                {
                        scannerTimer.restart();
                }
                else if (!scannerTimer.isRunning())
                {
                        scannerTimer.start();
                }
            }
            else
            {
                String input = getView().getBarcode();
                if(input.isEmpty())
                {
                    getView().enableItemAction(false);
                }
                else
                {
                    getView().enableItemAction(true);
                }   
            }
            
	}
	
	/**
	 * This method is called when the "Use Barcode Scanner" setting in the
	 * transfer item batch view is changed by the user.
	 */
	@Override
	public void useScannerChanged() 
    {
        if (getView().getUseScanner() != scannerEnabled)
		{
			getView().setBarcode("");
			getView().enableItemAction(false);
			scannerEnabled = getView().getUseScanner();
		}
	}
	
	/**
	 * This method is called when the selected product changes
	 * in the transfer item batch view.
	 */
	@Override
	public void selectedProductChanged() 
    {
		String bCode = getView().getSelectedProduct().getBarcode();
		Product product = model.getProduct(bCode);
		
		ArrayList<ItemData> items = transferredItems.get(product);
		
		ItemData[] itemDataToBeTransfered = new ItemData[items.size()];
		
		for(int i = 0; i < itemDataToBeTransfered.length; i++)
		{
		    itemDataToBeTransfered[i] = items.get(i);
		}
		getView().setItems(itemDataToBeTransfered);
	}
	
	/**
	 * This method is called when the user clicks the "Transfer Item" button
	 * in the transfer item batch view.
	 */
	@Override
	public void transferItem() 
    {
        String bCode = getView().getBarcode();
        
        Item i;
        try{
        	i = model.getItem(bCode);
        }
        catch (Exception e)
        {
        	i = null;
        }
        
        if(i == null)
        {
            getView().displayErrorMessage("The specified item does not exist");
            clearInputBox();
            return;
        }
        if(addToTransferredItemMap(i))
        {	
	        
	        
	        //use Inventory Controller(?)
	        //InventoryController.getInstance().moveItemToContainer(convertToItemData(i), targetPC);
	        try
	        {
	            model.transferItem(bCode, targetPC, model.getParentStorageUnit(targetPC));
	        }
	        catch(Exception e)
	        {
	            e.printStackTrace();
	        }
        }
        clearInputBox();
        loadValues();
        reloadItems();
		enableComponents();
	}
	
	/**
	 * This method is called when the user clicks the "Redo" button
	 * in the transfer item batch view.
	 */
	@Override
	public void redo() 
	{
		clearInputBox();
		Item i = model.redo();
		/*if(!productsEffected.contains(i.getProduct()))
			productsEffected.add(i.getProduct());*/
		if(!addToTransferredItemMap(i))
			getView().displayErrorMessage("Error redoing last transfer command");
		loadValues();
		reloadItems();
		enableComponents();
	}

	/**
	 * This method is called when the user clicks the "Undo" button
	 * in the transfer item batch view.
	 */
	@Override
	public void undo() 
	{
		clearInputBox();
		Item i = model.undo();
		removeFromTransferredItemMap(i);
		loadValues();
		reloadItems();
		enableComponents();
	}

	/**
	 * This method is called when the user clicks the "Done" button
	 * in the transfer item batch view.
	 */
	@Override
	public void done() {
		getView().close();
		model.clearHistory();
	}

    private boolean addToTransferredItemMap(Item i)
    {
        Product p = i.getProduct();
        ItemData id = convertToItemData(i);
        
        if(transferredItems.containsKey(p))
        {
            ArrayList<ItemData> al = transferredItems.get(p);
            if(al.contains(id))
            {
            	getView().displayErrorMessage("The specified item has already been transferred");
            	return false;
            }
            else
            	al.add(id);
        }
        else
        {
            ArrayList<ItemData> al = new ArrayList<ItemData>();
            al.add(id);
            transferredItems.put(p, al);
            productsEffected.add(p);
        }
        return true;
    }
    
    private void removeFromTransferredItemMap(Item i)
    {
    	Product p = i.getProduct();
        ItemData id = convertToItemData(i);
        
        if(transferredItems.containsKey(p))
        {
            ArrayList<ItemData> al = transferredItems.get(p);
            boolean result = al.remove(id);
            if(result == false)
            	getView().displayErrorMessage("Error removing item from" + 
            								" maps: TransferItemBatchController");
            if(al.size()==0)
            {
            	productsEffected.remove(p);
            	transferredItems.remove(p);
            }
        }
        else
        {
            ArrayList<ItemData> al = new ArrayList<ItemData>();
            al.add(id);
            transferredItems.put(p, al);
            productsEffected.add(p);
        }
    }
        
    /*    private ProductData convertToProductData(Product p)
	{
		ProductData newData = new ProductData();
		
		newData.setBarcode(p.getBarcodeString());
		
		newData.setDescription(p.getDescription());
		newData.setShelfLife(String.valueOf( p.getShelfLife() ) );
		newData.setSize(p.getThreeMonthSupply() + "");
		newData.setSupply(String.valueOf( p.getThreeMonthSupply() ) );
                
                int sizeOfProductItems = transferedItems.get(p).size();
                newData.setCount(sizeOfProductItems + "");
		
		return newData;
	}*/
        
    /*    private ItemData convertToItemData(Item i)
	{
		ItemData newData = new ItemData();

		newData.setBarcode(i.getBarcodeString());
		newData.setEntryDate(i.getEntryDate().getTime());
		if (i.getExpirationDate() != null)
		{
			newData.setExpirationDate(i.getExpirationDate().getTime());
		}
		else
		{
			newData.setExpirationDate(null);
		}
                
                ContainerType type = i.getProductContainer().getContainerType();
                if(type.equals(ContainerType.STORAGE_UNIT))
                {
                    newData.setStorageUnit(i.getProductContainer().getName());
                    newData.setProductGroup("");
                }
                else
                {
                    newData.setProductGroup(i.getProductContainer().getName());
                    newData.setStorageUnit(
                    model.getParentStorageUnit(i.getProductContainer()).getName());
                }
		return newData;
	}*/
        
    private void clearInputBox()
    {
        getView().setBarcode("");
        getView().enableItemAction(false);
        scannerTimer.stop();
    }
}

